﻿// 2022/4/1: 首个版本
// 2022/4/11: 增加VideoRecordConfigTable
// 2022/11/8: 增加DAT文件格式，增加YUV422、RAW编码格式支持
// 2022/11/14: 增加RAW12/14编码格式支持
// 2023/2/15: 增加RAW16/Y16编码格式支持
// 2023/2/20: 支持远程采集模式
// 2023/4/7: YUV420/YUV411的默认存储格式改为DAT
// 2023/4/10: 增加Load/Save方法
// 2023/4/23: 增加RecordLegacyStyle字段

using System;
using System.Collections.Generic;
using System.Xml;
using System.Linq;
using ASEva;
using ASEva.Utility;
using SO = System.Reflection.ObfuscationAttribute;

namespace CommonSource
{
    [SO]
    enum VideoRecordFormat
    {
        AVI = 1,
        MP4 = 2,
        MKV = 3,
        DAT = 4,
    }

    class VideoRecordConfig
    {
        public VideoRecordFormat Format { get; set; }
    }

    class DefaultVideoFileIOConfig : ModuleConfig
    {
        public bool OnlineWriteVideo { get; set; }
        public bool FromRawReadVideo { get; set; }
        public bool OfflineFromGenReadVideo { get; set; }
        public bool ReplayFromGenReadVideo { get; set; }

        public int[] WriteVideoSubIndex { get; set; } // -1: main data path

        public Dictionary<VideoDataCodec, VideoRecordConfig> VideoRecordConfigTable { get; set; }

        public bool RecordLegacyStyle { get; set; }

        private Dictionary<VideoRecordFormat, VideoDataCodec[]> availableRecordFormatTable;
        public Dictionary<VideoRecordFormat, VideoDataCodec[]> AvailableRecordFormatTable
        {
            get { return availableRecordFormatTable; }
        }

        public DefaultVideoFileIOConfig()
        {
            availableRecordFormatTable = new Dictionary<VideoRecordFormat, VideoDataCodec[]>();
            availableRecordFormatTable[VideoRecordFormat.AVI] = new VideoDataCodec[]
                {
                    VideoDataCodec.H264,
                    VideoDataCodec.H265,
                    VideoDataCodec.MJPEG,
                    VideoDataCodec.YUV411,
                    VideoDataCodec.YUV420,
                };
            availableRecordFormatTable[VideoRecordFormat.MP4] = new VideoDataCodec[]
                {
                    VideoDataCodec.H264,
                    VideoDataCodec.H265,
                    VideoDataCodec.MJPEG,
                };
            availableRecordFormatTable[VideoRecordFormat.MKV] = new VideoDataCodec[]
                {
                    VideoDataCodec.H264,
                    VideoDataCodec.H265,
                    VideoDataCodec.MJPEG,
                    VideoDataCodec.YUV411,
                    VideoDataCodec.YUV420,
                };
            availableRecordFormatTable[VideoRecordFormat.DAT] = new VideoDataCodec[]
                {
                    VideoDataCodec.H264,
                    VideoDataCodec.H265,
                    VideoDataCodec.MJPEG,
                    VideoDataCodec.YUV411,
                    VideoDataCodec.YUV420,
                    VideoDataCodec.YUV422,
                    VideoDataCodec.RAW,
                    VideoDataCodec.RAW12,
                    VideoDataCodec.RAW14,
                    VideoDataCodec.RAW16,
                    VideoDataCodec.Y16,
                };

            ResetToDefault();
        }

        public void ResetToDefault()
        {
            OnlineWriteVideo = true;
            FromRawReadVideo = true;
            OfflineFromGenReadVideo = true;
            ReplayFromGenReadVideo = true;
            WriteVideoSubIndex = new int[24];
            for (int i = 0; i < 24; i++) WriteVideoSubIndex[i] = -1;

            VideoRecordConfigTable = new Dictionary<VideoDataCodec, VideoRecordConfig>();
            VideoRecordConfigTable[VideoDataCodec.H264] = new VideoRecordConfig { Format = VideoRecordFormat.AVI };
            VideoRecordConfigTable[VideoDataCodec.H265] = new VideoRecordConfig { Format = VideoRecordFormat.AVI };
            VideoRecordConfigTable[VideoDataCodec.MJPEG] = new VideoRecordConfig { Format = VideoRecordFormat.AVI };
            VideoRecordConfigTable[VideoDataCodec.YUV411] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.YUV420] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.YUV422] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.RAW] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.RAW12] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.RAW14] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.RAW16] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };
            VideoRecordConfigTable[VideoDataCodec.Y16] = new VideoRecordConfig { Format = VideoRecordFormat.DAT };

            RecordLegacyStyle = false;
        }

        public static DefaultVideoFileIOConfig Load(object caller)
        {
            var config = new DefaultVideoFileIOConfig();
            var configString = Agency.GetModuleConfig(caller, "default-video-file-io.plugindefaultvideofileio");
            if (configString == null) return null;
            config.SetConfig(configString);
            return config;
        }

        public void Save(object caller)
        {
            Agency.SetModuleConfig(caller, "default-video-file-io.plugindefaultvideofileio", GetConfig());
        }

        public override string GetConfig()
        {
            var xml = Xml.Create("root");
            var cw = new AttributeWriter(xml.DocumentElement);

            cw.WriteBool("online_write_video", OnlineWriteVideo, "yes", "no");
            cw.WriteBool("from_raw_read_video", FromRawReadVideo, "yes", "no");
            cw.WriteBool("offline_from_gen_read_video", OfflineFromGenReadVideo, "yes", "no");
            cw.WriteBool("replay_from_gen_read_video", ReplayFromGenReadVideo, "yes", "no");
            cw.WriteString("write_video_subindex", String.Join(',', WriteVideoSubIndex));

            foreach (var pair in VideoRecordConfigTable)
            {
                var prefix = pair.Key.ToString().ToLower() + "_";
                cw.WriteInt(prefix + "rec_format", (int)pair.Value.Format);
            }

            cw.WriteBool("record_legacy_style", RecordLegacyStyle, "yes", "no");

            return xml.InnerXml;
        }

        public override void SetConfig(string config)
        {
            if (String.IsNullOrEmpty(config)) return;

            var xml = new XmlDocument();
            xml.LoadXml(config);

            var cp = new AttributeParser(xml.DocumentElement);

            OnlineWriteVideo = cp.ParseBool("online_write_video", "yes", true);
            FromRawReadVideo = cp.ParseBool("from_raw_read_video", "yes", true);
            OfflineFromGenReadVideo = cp.ParseBool("offline_from_gen_read_video", "yes", false);
            ReplayFromGenReadVideo = cp.ParseBool("replay_from_gen_read_video", "yes", true);

            var subIndexString = cp.ParseString("write_video_subindex", "");
            if (!String.IsNullOrEmpty(subIndexString))
            {
                var subIndexComps = subIndexString.Split(',');
                for (int i = 0; i < 24; i++)
                {
                    if (i >= subIndexComps.Length) WriteVideoSubIndex[i] = -1;
                    else WriteVideoSubIndex[i] = Convert.ToInt32(subIndexComps[i]);
                    WriteVideoSubIndex[i] = Math.Max(-1, Math.Min(3, WriteVideoSubIndex[i]));
                }
            }
            else
            {
                for (int i = 0; i < 24; i++) WriteVideoSubIndex[i] = -1;
            }

            foreach (var type in VideoRecordConfigTable.Keys)
            {
                var prefix = type.ToString().ToLower() + "_";
                int formatID = cp.ParseInt(prefix + "rec_format", 0);
                if (formatID == 0) continue;

                var format = (VideoRecordFormat)formatID;
                if (!availableRecordFormatTable.ContainsKey(format)) continue;
                if (!availableRecordFormatTable[format].Contains(type)) continue;

                VideoRecordConfigTable[type].Format = format;
            }

            RecordLegacyStyle = cp.ParseBool("record_legacy_style", "yes", false);
        }

        public override void DisableAll()
        {
            OnlineWriteVideo = false;
            FromRawReadVideo = false;
            OfflineFromGenReadVideo = false;
            ReplayFromGenReadVideo = false;
        }

        public override ConfigStatus GetConfigStatus()
        {
            switch (Agency.GetAppMode())
            {
            case ApplicationMode.Online:
            case ApplicationMode.Remote:
                if (OnlineWriteVideo)
                {
                    var subPaths = Agency.GetSubDataPathes();
                    foreach (var subIndex in WriteVideoSubIndex)
                    {
                        if (subIndex < 0) continue;
                        if (subIndex >= subPaths.Length) return ConfigStatus.EnabledWithError;
                        if (subPaths[subIndex] == null) return ConfigStatus.EnabledWithError;
                    }
                    return ConfigStatus.Enabled;
                }
                break;
            case ApplicationMode.Offline:
                if (FromRawReadVideo) return ConfigStatus.Enabled;
                if (OfflineFromGenReadVideo) return ConfigStatus.Enabled;
                break;
            case ApplicationMode.Replay:
                if (FromRawReadVideo) return ConfigStatus.Enabled;
                if (ReplayFromGenReadVideo) return ConfigStatus.Enabled;
                break;
            }
            return ConfigStatus.Disabled;
        }

        public override List<RecordDataType> GetRecordDataTypes()
        {
            var list = new List<RecordDataType>();
            switch (Agency.GetAppMode())
            {
            case ApplicationMode.Online:
            case ApplicationMode.Remote:
                if (OnlineWriteVideo) list.Add(RecordDataType.VideoRawData);
                break;
            }
            return list;
        }
    }
}